﻿//==============================================================================
// File: LightweightMetaPropertyManager.cs
// Created: 2010-05-10
// Author: Piotr Włodek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with WpfRcp. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using System.Collections.Generic;
using System.Diagnostics;
using WpfRcp.PropertyModel.Scope;
using System.Linq;
using Res = WpfRcp.PropertyModel.Properties.Resources;

namespace WpfRcp.PropertyModel.Core
{
    /// <summary>
    /// Lightweight implementation of the <see cref="IMetaPropertyManager"/> interface.
    /// </summary>
    /// <remarks>
    /// This implementation does not support <see cref="MetaPropertyKind.Dynamic"/> and <see cref="MetaPropertyKind.Attached"/>
    /// properties.
    /// </remarks>
    [Serializable]
    public class LightweightMetaPropertyManager : MetaPropertyManagerBase
    {
        /// <summary>
        /// Stores permanent and transient properties' values and metaproperties.
        /// </summary>
        private readonly IProperty[] m_Properties;

        #region Public Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="LightweightMetaPropertyManager"/> class.
        /// </summary>
        /// <param name="propertyContainer">An instance of a <see cref="IPropertyContainer"/>.</param>
        public LightweightMetaPropertyManager(IPropertyContainer propertyContainer) : base(propertyContainer)
        {
            if (propertyContainer == null)
                throw new ArgumentNullException("propertyContainer");

            var propertyContainerType = propertyContainer.GetType();
            var properties = PropertyManager.InitializeStaticProperties(propertyContainerType);

            m_Properties = new IProperty[properties.Count()];

            // Assigne defaults
            foreach (var metaProperty in properties)
            {
                PropertyMetadata metadata = metaProperty.GetMetadata(propertyContainerType);

                // ReSharper disable DoNotCallOverridableMethodsInConstructor
                m_Properties[metaProperty.Index] = CreateProperty(metaProperty, metadata);
                // ReSharper restore DoNotCallOverridableMethodsInConstructor
            }
        }

        #endregion

        #region Public Property Management

        protected override IEnumerable<MetaProperty> EnumerateMetaProperties()
        {
            foreach (var property in m_Properties)
            {
                yield return property.MetaProperty;
            }
        }

        public override MetaPropertyCollection GetDynamicMetaProperties()
        {
            return new MetaPropertyCollection();
        }

        public override void AddDynamicProperty(MetaProperty metaProperty)
        {
            throw new NotSupportedException();
        }

        public override void RemoveDynamicProperty(MetaProperty metaProperty)
        {
            throw new NotSupportedException();
        }

        #endregion

        #region Public Property Scope Management

        public override void AddPropertyScope(IPropertyScope scope)
        {
            throw new NotSupportedException();
        }

        public override void RemovePropertyScope(IPropertyScope scope)
        {
            throw new NotSupportedException();
        }

        #endregion

        #region Public GetProperty Implementation

        public override IEnumerable<IProperty> GetProperties()
        {
            foreach (var property in m_Properties)
            {
                yield return property;
            }
        }

        public override object GetPropertyValue(MetaProperty metaProperty)
        {
            if (metaProperty == null)
                throw new ArgumentNullException("metaProperty");

            return GetProperty(metaProperty).Value;
        }

        public override IProperty GetProperty(MetaProperty property)
        {
            if (property == null)
                throw new ArgumentNullException("property");

            switch (property.PropertyKind)
            {
                case MetaPropertyKind.Permanent:
                case MetaPropertyKind.Transient:

                    EnsureStaticProperty(property);
                    return GetStaticProperty(property);

                case MetaPropertyKind.Dynamic:
                case MetaPropertyKind.Attached:

                    throw new NotSupportedException();

                default:
                    return null;
            }
        }

        private IProperty GetStaticProperty(MetaProperty property)
        {
            try
            {
                return m_Properties[property.Index];
            }
            catch (IndexOutOfRangeException ex)
            {
                throw new MetaPropertyException(string.Format(Res.ExceptionPropertyNotFound, property.Name), ex);
            }
        }

        #endregion

        #region Private Validation

        [Conditional("DEBUG")]
        private void EnsureStaticProperty(MetaProperty property)
        {
            Debug.Assert(property != null);

            if (property.PropertyKind == MetaPropertyKind.Permanent ||
                property.PropertyKind == MetaPropertyKind.Transient)
            {
                foreach (var prop in m_Properties)
                {
                    if (prop.MetaProperty.Equals(property))
                        return;
                }

                throw new MetaPropertyException(string.Format(Res.ExceptionPropertyNotFound, property.Name));
            }
        }

        #endregion
    }
}